package com.sduept.i1;

import java.io.IOException;
import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.server.TioServer;
import org.tio.server.TioServerConfig;
import org.tio.server.intf.TioServerHandler;
import org.tio.server.intf.TioServerListener;

import com.sduept.i1.model.ClientManagement;
import com.sduept.i1.model.ImageStorageManagement;
import com.sduept.i1.model.MonitorPacket;
import com.sduept.i1.model.RemoteUpgradeData;
import com.sduept.i1.parse.Encoder;
import com.sduept.tool.CdzlbTools;

public class I1Server {
	private static final Logger logger = LoggerFactory.getLogger(I1Server.class);

	public static boolean bDebug = false;
	public static final String MONITOR_PACKET_KEY = "monitorPacket";
	public static ClientManagement clientManagement;
	public static ImageStorageManagement imageStorageManagement;
	public static TioServerHandler tioHandler;
	public static TioServerListener tioListener;
	public static TioServerConfig serverConfig;
	public static TioServer tioServer;

	private volatile static I1Server me = null;

	public static I1Server getInstance() {
		if (me == null) {
			synchronized (I1Server.class) {
				if (me == null) {
					me = new I1Server();
				}
			}
		}
		return me;
	}

	public TioServerConfig getServerTioConfig() {
		return serverConfig;
	}

	public static void start(String ip, int port) throws IOException {
		clientManagement = new ClientManagement();
		imageStorageManagement = new ImageStorageManagement();
		tioHandler = new I1ServerHandler();
		tioListener = new I1ServerListener();
		serverConfig = new TioServerConfig("TioSiteImServer", tioHandler, tioListener);
		tioServer = new TioServer(serverConfig);
		tioServer.start(ip, port);
	}

	public static void main(String[] args) {
		try {
			start("127.0.0.1", 5002);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void close() {
		tioServer.stop();
	}

	// 开启或关闭调试
	public static void debug(boolean b) {
		bDebug = b;
	}

	// 发送控制报文
	public static boolean sendPacket(String deviceId, MonitorPacket packet) {
		if (packet == null) {
			logger.error("I1Server.sendPacket: Make Packet Error in Device {}", deviceId);
			return false;
		}

		ChannelContext context = Tio.getByBsId(I1Server.getInstance().getServerTioConfig(), deviceId);
		if (context == null) {
			logger.error("I1Server.sendPacket: Cannot Find Device: {}", deviceId);
			return false;
		} else {
			logger.info("I1Server.sendPacket: Send Packet to Device {}", deviceId);
			return Tio.send(context, packet);
		}
	}

	// 监测装置时间查询
	// 03H-A1H
	public static void getClockTime(String deviceId) {
		sendPacket(deviceId, Encoder.makeClockTimePacket(deviceId, 0, ""));
	}

	// 监测装置时间设置
	// 03H-A1H
	// timeStr: yyyy-MM-dd HH:mm:ss
	public static void setClockTime(String deviceId, String timeStr) {
		sendPacket(deviceId, Encoder.makeClockTimePacket(deviceId, 1, timeStr));
	}

	// 监测装置网络适配器查询
	// 03H-A2H
	public static void getNetAdapter(String deviceId, int requestFlag) {
		sendPacket(deviceId, Encoder.makeNetAdapterPacket(deviceId, 0, requestFlag, null));
	}

	// 监测装置网络适配器设置
	// 03H-A2H
	public static void setNetAdapter(String deviceId, int requestFlag, String[] values) {
		sendPacket(deviceId, Encoder.makeNetAdapterPacket(deviceId, 1, requestFlag, values));
	}

	// 请求历史数据
	// 03H-A3H
	public static void getHistoryData(String deviceId, int requestType, String startTime, String endTime) {
		sendPacket(deviceId, Encoder.makeHistoryDataPacket(deviceId, requestType, startTime, endTime));
	}

	// 监测装置采样周期查询
	// 03H-A4H
	public static void getCollectPeriod(String deviceId, int requestType, int requestFlag) {
		sendPacket(deviceId, Encoder.makeCollectPeriodPacket(deviceId, 0, requestType, requestFlag, null));
	}

	// 监测装置采样周期设置
	// 03H-A4H
	public static void setCollectPeriod(String deviceId, int requestType, int requestFlag, int[] values) {
		sendPacket(deviceId, Encoder.makeCollectPeriodPacket(deviceId, 1, requestType, requestFlag, values));
	}

	// 模型参数配置信息查询
	// 03H-A5H
	public static void getParamConfig(String deviceId, int configTotal, String[] configName) {
		sendPacket(deviceId, Encoder.makeParamConfigPacket(deviceId, 0, configTotal, configName, null, null));
	}

	// 模型参数配置信息设置
	// 03H-A5H
	public static void setParamConfig(String deviceId, int configTotal, String[] configName, int[] configDataType,
			int[] configData) {
		sendPacket(deviceId,
				Encoder.makeParamConfigPacket(deviceId, 1, configTotal, configName, configDataType, configData));
	}

	// 报警阈值查询
	// 03H-A6H
	public static void getAlarmThreshold(String deviceId, int requestType, int alarmTotal, String[] alarmNames) {
		sendPacket(deviceId, Encoder.makeAlarmThresholdPacket(deviceId, 0, requestType, alarmTotal, alarmNames, null));
	}

	// 报警阈值设置
	// 03H-A6H
	public static void setAlarmThreshold(String deviceId, int requestType, int alarmTotal, String[] alarmNames,
			int[] alarmDatas) {
		sendPacket(deviceId,
				Encoder.makeAlarmThresholdPacket(deviceId, 0, requestType, alarmTotal, alarmNames, alarmDatas));
	}

	// 监测装置指向主站系统的信息查询
	// 03H-A7H
	public static void getDevicePoint(String deviceId, int requestFlag) {
		sendPacket(deviceId, Encoder.makeDevicePointPacket(deviceId, 0, requestFlag, null));
	}

	// 监测装置指向主站系统的信息设置
	// 03H-A7H
	public static void setDevicePoint(String deviceId, int requestFlag, String[] values) {
		sendPacket(deviceId, Encoder.makeDevicePointPacket(deviceId, 1, requestFlag, values));
	}

	// 基本信息查询
	// 03H-A8H
	public static void getInfoType(String deviceId, int requestType, int infoType) {
		sendPacket(deviceId, Encoder.makeInfoTypePacket(deviceId, 0, requestType, infoType));
	}

	// 基本信息设置
	// 03H-A8H
	public static void setBasicInfo(String deviceId, int requestType, int infoType) {
		sendPacket(deviceId, Encoder.makeInfoTypePacket(deviceId, 1, requestType, infoType));
	}

	// 发送远程升级数据报，结束后发送结束标记
	// 该方法会在发向设备的远程升级请求得到响应后自动执行，不需要浏览器端调用
	// 03H-A9H 03H-AAH
	public static void remoteUpgrade(String deviceId, String fileName) {
		ArrayList<MonitorPacket> packetArray = Encoder.makeFilePacketArray(deviceId, fileName);

		try {
			int packetNo = packetArray.size();
			for (MonitorPacket packet : packetArray) {
				sendPacket(deviceId, packet);
			}
			Thread.sleep(2000);

			clientManagement.newConnection(deviceId);
			for (int i = 0; i < 5; i++) {
				sendPacket(deviceId, Encoder.makeFileEndPackage(deviceId, fileName, packetNo));
				Thread.sleep(3000);

				// 检查是否接收到设备的响应
				if (clientManagement.checkResponse(deviceId)) {
					break;
				}
			}
		} catch (InterruptedException e) {
			logger.error("I1Server.remoteUpgrade: Thread Exception");
		}
	}

	// 发送远程升级补包数据
	// 该方法会在得到设备的补包请求后自动执行，不需要浏览器端调用
	// 03H-ABH 03H-AAH
	public static void remoteUpgradeComplement(String deviceId, String fileName, int[] complementPackNo) {
		ArrayList<MonitorPacket> packetArray = Encoder.makeFileComplementPacketArray(deviceId, fileName,
				complementPackNo);

		try {
			for (MonitorPacket packet : packetArray) {
				sendPacket(deviceId, packet);
			}
			Thread.sleep(2000);

			clientManagement.newConnection(deviceId);
			byte[] fileContent = CdzlbTools.getContent(fileName);
			int maxSampleLength = RemoteUpgradeData.dataLength;
			int packetNo = fileContent.length / maxSampleLength;

			if (fileContent.length % maxSampleLength > 0) {
				packetNo++;
			}

			for (int i = 0; i < 5; i++) {
				I1Server.sendPacket(deviceId, Encoder.makeFileEndPackage(deviceId, fileName, packetNo));
				Thread.sleep(3000);

				// 检查是否接收到设备的响应
				if (clientManagement.checkResponse(deviceId)) {
					break;
				}
			}
		} catch (IOException e) {
			logger.error("Encoder.makeFilePacketArray: Get File Content Error");
		} catch (InterruptedException e) {
			logger.error("I1Server.remoteUpgrade: Thread Exception");
		}
	}

	// 监测装置编码查询
	// 03H-ACH
	public static void getDeviceId(String deviceId, int requestFlag) {
		sendPacket(deviceId, Encoder.makeDeviceIdPacket(deviceId, 0, requestFlag, null));
	}

	// 监测装置编码设置
	// 03H-ACH
	public static void setDeviceId(String deviceId, int requestFlag, String[] values) {
		sendPacket(deviceId, Encoder.makeDeviceIdPacket(deviceId, 1, requestFlag, values));
	}

	// 复位
	// 03H-ADH
	public static void reset(String deviceId, int resetMode) {
		sendPacket(deviceId, Encoder.makeResetPacket(deviceId, resetMode));
	}

	// 监测装置苏醒时间设置
	// 03H-AEH
	public static void setAwakeTime(String deviceId, String timeStr, int revivalCycle, int durationTime) {
		sendPacket(deviceId, Encoder.makeAwakeTimePacket(deviceId, timeStr, revivalCycle, durationTime));
	}

	// 发送远程升级数据报请求
	// 03H-B8H
	public static void requestRemoteUpgrade(String deviceId) {
		clientManagement.newConnection(deviceId);
		try {
			for (int i = 0; i < 5; i++) {
				sendPacket(deviceId, Encoder.makeRemoteUpdateRequestPacket(deviceId, RemoteUpgradeData.fileName,
						RemoteUpgradeData.version));
				Thread.sleep(3000);

				if (clientManagement.checkResponse(deviceId)) {
					break;
				}
			}
		} catch (InterruptedException e) {
			logger.error("I1Server.requestRemoteUpgrade: Thread Exception");
		}
	}

	// 装置功能启停
	// 03H-B9H
	public static void functionControl(String deviceId, int functionType, int control) {
		sendPacket(deviceId, Encoder.makeFunctionControlPacket(deviceId, functionType, control));
	}

	// http升级文件推送
	// 03H-BAH
	public static void httpUpgradePush(String deviceId, int fileType, String httpUrl, String patchPath) {
		sendPacket(deviceId, Encoder.makeHttpUpgradePushPacket(deviceId, fileType, httpUrl, patchPath));
	}

	// 摄像机3D控球调节命令
	// 03H-BBH
	public static void threeDBallControl(String deviceId, int channelNo, int startX, int startY, int endX, int endY) {
		sendPacket(deviceId, Encoder.make3DBallControlPacket(deviceId, channelNo, startX, startY, endX, endY));
	}

	// 摄像机巡检配置
	// 03H-BCH
	public static void setCameraCruise(String deviceId, int channelNo, int cmd, int cruiseNo, int pointerNo) {
		sendPacket(deviceId, Encoder.makeCameraCruiseSetPacket(deviceId, channelNo, cmd, cruiseNo, pointerNo));
	}

	// 摄像机巡检查询
	// 03H-BDH
	public static void getCameraCruise(String deviceId, int channelNo) {
		sendPacket(deviceId, Encoder.makeCameraCruiseGetPacket(deviceId, channelNo));
	}

	// 摄像机巡检策略查询
	// 03H-BEH
	public static void getCameraCruiseStrategy(String deviceId, int channelNo, int configNum, int[] enables,
			int[] cruiseNos) {
		sendPacket(deviceId, Encoder.makeCameraCruiseStrategyPacket(deviceId, channelNo, 0, configNum, enables,
				cruiseNos, null, null, null, null));
	}

	// 摄像机巡检策略配置设置
	// 03H-BEH
	public static void setCameraCruiseStrategy(String deviceId, int channelNo, int configNum, int[] enables,
			int[] cruiseNos, int[] cruiseNums, int[] startHours, int[] startMinutes, int[] startSeconds) {
		sendPacket(deviceId, Encoder.makeCameraCruiseStrategyPacket(deviceId, channelNo, 1, configNum, enables,
				cruiseNos, cruiseNums, startHours, startMinutes, startSeconds));
	}

	// 摄像机远程调节命令
	// 07H-D0H
	public static void remoteAction(String deviceId, int channelNo, int presettingNo, int action) {
		sendPacket(deviceId, Encoder.makeRemoteActionPacket(deviceId, channelNo, presettingNo, action));
	}

	// 图像参数查询
	// 07H-EBH
	public static void getImageParam(String deviceId, int requestFlag, int channelNo, int preset) {
		sendPacket(deviceId, Encoder.makeImageParamPacket(deviceId, 0, requestFlag, null, channelNo, preset));
	}

	// 图像参数设置
	// 07H-EBH
	public static void setImageParam(String deviceId, int requestFlag, int[] values, int channelNo, int preset) {
		sendPacket(deviceId, Encoder.makeImageParamPacket(deviceId, 1, requestFlag, values, channelNo, preset));
	}

	// 短视频参数查询
	// 07H-ECH
	public static void getVideoParam(String deviceId, int channelNo) {
		sendPacket(deviceId, Encoder.makeVideoParamPacket(deviceId, 0, channelNo, 0, 0));
	}

	// 短视频参数设置
	// 07H-ECH
	public static void setVideoParam(String deviceId, int channelNo, int videoFormat, int videoTime) {
		sendPacket(deviceId, Encoder.makeVideoParamPacket(deviceId, 1, channelNo, videoFormat, videoTime));
	}

	// 拍摄时间查询
	// 07H-EDH
	public static void getShootTime(String deviceId, int channelNo) {
		sendPacket(deviceId, Encoder.makeShootTimePacket(deviceId, 0, channelNo, 0, 0, 0, 0, 0, 0));
	}

	// 拍摄时间设置
	// 07H-EDH
	public static void setShootTime(String deviceId, int channelNo, int hour1, int minute1, int hour2, int minute2,
			int hour3, int minute3) {
		sendPacket(deviceId,
				Encoder.makeShootTimePacket(deviceId, 0, channelNo, hour1, minute1, hour2, minute2, hour3, minute3));
	}

	// 手动请求拍摄图像/短视频报文
	// 07H-EEH
	public static void shootImage(String deviceId, int channelNo, int dataType) {
		sendPacket(deviceId, Encoder.makeRequestShootPacket(deviceId, channelNo, dataType));
	}

	// OSD查询
	// 07H-F3H
	public static void getOsd(String deviceId, int channelNo) {
		sendPacket(deviceId, Encoder.makeOsdPacket(deviceId, channelNo, 0, 0, 0, ""));
	}

	// OSD设置
	// 07H-F3H
	public static void setOsd(String deviceId, int channelNo, int showTime, int showText, String textContent) {
		sendPacket(deviceId, Encoder.makeOsdPacket(deviceId, channelNo, 1, showTime, showText, textContent));
	}

	// 图像分析参数设置
	// 07H-F5H
	public static void setImageAnalysisParam(String deviceId, int channelNo, int preset, int analysisFlag, int alarmNum,
			int[] alarmTypes, int[] alarmThresholds, int alarmAreaNum, int[] areaFlags, int[] areaPointNums,
			int[][] areaPointXs, int[][] areaPointYs) {
		sendPacket(deviceId,
				Encoder.makeImageAnalysisParamSetPacket(deviceId, channelNo, preset, analysisFlag, alarmNum, alarmTypes,
						alarmThresholds, alarmAreaNum, areaFlags, areaPointNums, areaPointXs, areaPointYs));
	}
	
	// 图像分析参数查询
	// 07H-F6H
	public static void getImageAnalysisParam(String deviceId, int paramNum, int[] channelNos, int[] presets) {
		sendPacket(deviceId, Encoder.makeImageAnalysisParamGetPacket(deviceId, paramNum, channelNos, presets));
	}
	
	// 监测装置基本信息补充查询
	// 0AH-FAH
	public static void getAdditionalBasicInfo(String deviceId) {
		sendPacket(deviceId, Encoder.makeAdditionalBasicInfoGetPacket(deviceId));
	}
	
	// 监测装置工作状态补充查询
	// 0A-FB
	public static void getAdditionalWorkState(String deviceId) {
		sendPacket(deviceId, Encoder.makeAdditionalWorkStateGetPacket(deviceId));
	}
}
